// 版权所有2010 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 此文件包含对ASTs的打印支持。

package ast

import (
	"fmt"
	"go/token"
	"io"
	"os"
	"reflect"
)

// 可以向Fprint提供一个字段过滤器来控制输出。
type FieldFilter func(name string, value reflect.Value) bool

// NotNilFilter对于非零的字段值返回true；
// 否则返回false。
func NotNilFilter(_ string, v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
		return !v.IsNil()
	}
	return true
}

// Fprint打印从AST node x开始到w的（子）树。
// If fset！=无，位置信息相对于该文件集
// 进行解释。否则，位置将打印为整数
// 值（文件集特定偏移量）。
// 
// 可以提供非零字段过滤器f来控制输出：
// 打印f（字段名、字段值）为真的结构字段；从输出中过滤所有其他内容。未报告的
// 永远不会打印结构字段。
func Fprint(w io.Writer, fset *token.FileSet, x any, f FieldFilter) error {
	return fprint(w, fset, x, f)
}

func fprint(w io.Writer, fset *token.FileSet, x any, f FieldFilter) (err error) {
	// 设置打印机
	p := printer{
		output: w,
		fset:   fset,
		filter: f,
		ptrmap: make(map[any]int),
		last:   '\n', // 强制打印第一行的行号
	}

	// 安装错误处理程序
	defer func() {
		if e := recover(); e != nil {
			err = e.(localError).err // 如果不是本地错误
		}
	}()

	// 打印x 
	if x == nil {
		p.printf("nil\n")
		return
	}
	p.print(reflect.ValueOf(x))
	p.printf("\n")

	return
}

// 打印x到标准输出，跳过零字段。
// Print（fset，x）与Fprint（os.Stdout，fset，x，NotNilFilter）相同。
func Print(fset *token.FileSet, x any) error {
	return Fprint(os.Stdout, fset, x, NotNilFilter)
}

type printer struct {
	output io.Writer
	fset   *token.FileSet
	filter FieldFilter
	ptrmap map[any]int // /*T->行号
	indent int         // 当前缩进级别
	last   byte        // 写入
	line   int         // 当前行号
}

var indent = []byte(".  ")

func (p *printer) Write(data []byte) (n int, err error) {
	var m int
	for i, b := range data {
		// 不变量：数据[0:n]已写入
		if b == '\n' {
			m, err = p.output.Write(data[n : i+1])
			n += m
			if err != nil {
				return
			}
			p.line++
		} else if p.last == '\n' {
			_, err = fmt.Fprintf(p.output, "%6d  ", p.line)
			if err != nil {
				return
			}
			for j := p.indent; j > 0; j-- {
				_, err = p.output.Write(indent)
				if err != nil {
					return
				}
			}
		}
		p.last = b
	}
	if len(data) > n {
		m, err = p.output.Write(data[n:])
		n += m
	}
	return
}

// localError包装了本地捕获的错误，因此我们可以区分
// 它们与我们不想返回的真正恐慌错误。
type localError struct {
	err error
}

// printf是一种方便的包装器，用于处理打印错误。
func (p *printer) printf(format string, args ...any) {
	if _, err := fmt.Fprintf(p, format, args...); err != nil {
		panic(localError{err})
	}
}

// 实现说明：打印是为AST节点编写的，但可以是
// 用于打印任意数据结构；这样的版本应该是
// 可能在另一个包中。
// 
// 注意：此代码检测通过指针创建的（部分）循环，但检测不到通过包含
// 相同切片或映射的切片或映射创建的循环。一般数据结构的代码可能
// 也应该包含这些内容。

func (p *printer) print(x reflect.Value) {
	if !NotNilFilter("", x) {
		p.printf("nil")
		return
	}

	switch x.Kind() {
	case reflect.Interface:
		p.print(x.Elem())

	case reflect.Map:
		p.printf("%s (len = %d) {", x.Type(), x.Len())
		if x.Len() > 0 {
			p.indent++
			p.printf("\n")
			for _, key := range x.MapKeys() {
				p.print(key)
				p.printf(": ")
				p.print(x.MapIndex(key))
				p.printf("\n")
			}
			p.indent--
		}
		p.printf("}")

	case reflect.Pointer:
		p.printf("*")
		// 类型检查的AST可能包含循环-使用ptrmap 
		// 跟踪已打印的对象
		// 并打印相应的行号而不是
		ptr := x.Interface()
		if line, exists := p.ptrmap[ptr]; exists {
			p.printf("(obj @ %d)", line)
		} else {
			p.ptrmap[ptr] = p.line
			p.print(x.Elem())
		}

	case reflect.Array:
		p.printf("%s {", x.Type())
		if x.Len() > 0 {
			p.indent++
			p.printf("\n")
			for i, n := 0, x.Len(); i < n; i++ {
				p.printf("%d: ", i)
				p.print(x.Index(i))
				p.printf("\n")
			}
			p.indent--
		}
		p.printf("}")

	case reflect.Slice:
		if s, ok := x.Interface().([]byte); ok {
			p.printf("%#q", s)
			return
		}
		p.printf("%s (len = %d) {", x.Type(), x.Len())
		if x.Len() > 0 {
			p.indent++
			p.printf("\n")
			for i, n := 0, x.Len(); i < n; i++ {
				p.printf("%d: ", i)
				p.print(x.Index(i))
				p.printf("\n")
			}
			p.indent--
		}
		p.printf("}")

	case reflect.Struct:
		t := x.Type()
		p.printf("%s {", t)
		p.indent++
		first := true
		for i, n := 0, t.NumField(); i < n; i++ {
			// 排除未导出的字段，因为它们的
			// 值无法通过反射访问
			if name := t.Field(i).Name; IsExported(name) {
				value := x.Field(i)
				if p.filter == nil || p.filter(name, value) {
					if first {
						p.printf("\n")
						first = false
					}
					p.printf("%s: ", name)
					p.print(value)
					p.printf("\n")
				}
			}
		}
		p.indent--
		p.printf("}")

	default:
		v := x.Interface()
		switch v := v.(type) {
		case string:
			// 打印引号中的字符串
			p.printf("%q", v)
			return
		case token.Pos:
			// 如果我们有一个文件集
			if p.fset != nil {
				p.printf("%s", p.fset.Position(v))
				return
			}
		}
		// 默认
		p.printf("%v", v)
	}
}
